﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Windows.Input;
using System.Windows.Forms;
using System.ComponentModel;
using Cheetah_Fast_Banking_App.Cryptography;

namespace Cheetah_Fast_Banking_App
{
    public class CommandRelayer : INotifyPropertyChanged
    {
        #region Properties
        /// <summary>
        /// Currently Selected account.  Used for databinding.
        /// OnProperty triggers update to databound objects
        /// </summary>
        public Account SelectedAccount
        {
            get { return _selectedAccount; }
            set
            {
                _selectedAccount = value;
                OnPropertyChanged("SelectedAccount");
            }
        }
        private Account _selectedAccount = null;

        public bool IsValidAccount
        {
            get { return _selectedAccount != null; }
        }
        #endregion // Properties

        #region Constructor

        /// <summary>
        /// Parameterless for Databinding with XAML
        /// </summary>
        public CommandRelayer()
        {
            bool success = DataSourceSingleton.GetInstance().SetDataSource(Properties.Settings.Default.LastXmlPath);
        }

        #endregion // Constructor

        #region RelayCommand Methods

        #region Transaction
        #region Add Transaction
        /// <summary>
        /// Method to determine when a new transaction can be created
        /// </summary>
        /// <returns>bool indicating if action is enabled</returns>
        private bool CanCreateNewTransactionExecute()
        {
            //TODO: Change create based on user permission rather than any time there is a current account
            return _selectedAccount != null;
        }

        /// <summary>
        /// Execute method for new transaction event
        /// </summary>
        private void NewTransactionExecute()
        {
            TransactionForm TransForm = new TransactionForm();
            TransForm.Text = "New Transaction";
            DialogResult Result = TransForm.ShowDialog();
            if (Result == DialogResult.OK)
            {
                _selectedAccount.SaveNewTransaction(TransForm.GetTransaction());
                TransForm.Dispose();
            }
        }

        /// <summary>
        /// ICommand for NewTransaction
        /// </summary>
        public ICommand UpdateNewTransaction
        {
            get { return new RelayCommand(NewTransactionExecute, CanCreateNewTransactionExecute); }
        }
        #endregion

        #region Edit Transaction
        /// <summary>
        /// Method to determine when a transaction can be edited
        /// </summary>
        /// <returns>bool indicating if action is enabled</returns>
        private bool CanEditTransactionExecute(object transaction)
        {
            //TODO: Change create based on user permission rather than any time there is a current account
            return _selectedAccount != null;
        }

        /// <summary>
        /// Execute method for edit transaction event
        /// </summary>
        private void EditTransactionExecute(object transaction)
        {
            Transaction trans = transaction as Transaction;
            if (trans == null)
                return;

            TransactionForm TransForm = new TransactionForm(trans);
            TransForm.Text = "Edit Transaction";
            DialogResult Result = TransForm.ShowDialog();
            if (Result == DialogResult.OK)
            {
                _selectedAccount.ModifyTransaction(TransForm.GetTransaction());
                TransForm.Dispose();
            }
        }

        /// <summary>
        /// ICommand for EditTransaction
        /// </summary>
        public ICommand UpdateEditTransaction
        {
            get { return new RelayCommand<object>(EditTransactionExecute, CanEditTransactionExecute); }
        }
        #endregion

        #region Delete Transaction
        /// <summary>
        /// Determines when a transaction can be deleted
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        bool CanDeleteTransaction(object transaction)
        {
            // Only allow deleting when account is logged into
            return _selectedAccount != null;
        }

        /// <summary>
        /// Method to delete a transaction
        /// </summary>
        /// <param name="transaction">transaction to delete</param>
        void DeleteTransactionExecute(object transaction)
        {
            Transaction trans = transaction as Transaction;
            if ((trans != null) && (_selectedAccount != null))
            {
                _selectedAccount.DeleteTransaction(trans);
            }
        }

        /// <summary>
        /// ICommand for Delete Transaction
        /// </summary>
        public ICommand UpdateDeleteTransaction
        {
            get { return new RelayCommand<object>(DeleteTransactionExecute, CanDeleteTransaction); }
        }
        #endregion 
        #endregion

        #region Bill Pay
        #region Add Bill Pay
        /// <summary>
        /// Method to determine when a new billpay can be created
        /// </summary>
        /// <returns>bool indicating if action is enabled</returns>
        private bool CanCreateNewBillPayExecute()
        {
            //TODO: Change create based on user permission rather than any time there is a current account
            return _selectedAccount != null;
        }

        /// <summary>
        /// Execute method for new billpay event
        /// </summary>
        private void NewBillPayExecute()
        {
            BillPayForm bpForm = new BillPayForm();
            if (bpForm.ShowDialog() == DialogResult.OK)
            {
                _selectedAccount.SaveNewBillPay(bpForm.GetBillPay());
                bpForm.Dispose();
            }
        }

        /// <summary>
        /// ICommand for NewBillPay
        /// </summary>
        public ICommand UpdateNewBillPay
        {
            get { return new RelayCommand(NewBillPayExecute, CanCreateNewBillPayExecute); }
        }
        #endregion

        #region Edit Bill Pay
        /// <summary>
        /// Method to determine when a bill pay can be edited
        /// </summary>
        /// <returns>bool indicating if action is enabled</returns>
        private bool CanEditBillPayExecute(object billpay)
        {
            //TODO: Change create based on user permission rather than any time there is a current account
            return _selectedAccount != null;
        }

        /// <summary>
        /// Execute method for edit bill pay event
        /// </summary>
        private void EditBillPayExecute(object billpay)
        {
            BillPay bill = billpay as BillPay;
            if (bill == null)
                return;

            BillPayForm bpForm = new BillPayForm(bill);
            if (bpForm.ShowDialog() == DialogResult.OK)
            {
                _selectedAccount.ModifyBillPay(bpForm.GetBillPay());
                bpForm.Dispose();
            }
        }

        /// <summary>
        /// ICommand for EditBillPay
        /// </summary>
        public ICommand UpdateEditBillPay
        {
            get { return new RelayCommand<object>(EditBillPayExecute, CanEditBillPayExecute); }
        }
        #endregion

        #region Delete Bill Pay
        /// <summary>
        /// Determines when a bill pay can be deleted
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        bool CanDeleteBillPay(object billpay)
        {
            // Only allow deleting when account is logged into
            return _selectedAccount != null;
        }

        /// <summary>
        /// Method to delete a bill pay
        /// </summary>
        /// <param name="billpay">bill pay to delete</param>
        void DeleteBillPayExecute(object billpay)
        {
            BillPay bill = billpay as BillPay;
            if ((bill != null) && (_selectedAccount != null))
            {
                _selectedAccount.DeleteBillPay(bill);
            }
        }

        /// <summary>
        /// ICommand for Delete Bill Pay
        /// </summary>
        public ICommand UpdateDeleteBillPay
        {
            get { return new RelayCommand<object>(DeleteBillPayExecute, CanDeleteBillPay); }
        }
        #endregion 

        #region Run BillPay
        /// <summary>
        /// Determines when a bill pay can be run
        /// </summary>
        /// <param name="parameter"></param>
        /// <returns></returns>
        bool CanRunBillPay()
        {
            // Only allow logged in Admin to run BillPay
            return _selectedAccount != null && _selectedAccount.IsAdmin;
        }

        /// <summary>
        /// Method to run bill pay
        /// </summary>
        void RunBillPayExecute()
        {
            BillPayManager bpm = new BillPayManager();
            bpm.RunBillPay();
        }

        /// <summary>
        /// ICommand for Run Bill Pay
        /// </summary>
        public ICommand UpdateRunBillPay
        {
            get { return new RelayCommand(RunBillPayExecute, CanRunBillPay); }
        }
        #endregion
        #endregion

        #region Login
        /// <summary>
        /// Determines when Login is enabled
        /// </summary>
        /// <returns>bool</returns>
        public bool CanLoginExecute()
        {
            return !IsValidAccount;
        }

        /// <summary>
        /// Executes login procedures
        /// </summary>
        private void LoginExecute()
        {
            FormLogin login = new FormLogin();

            do
            {
                DialogResult result = login.ShowDialog();
                if (result != DialogResult.OK)
                {
                    // User closed the window--exit the login process
                    break;
                }

                // Login button pressed--validate credentials
                // 1. validate user exists
                if (!DataSourceSingleton.GetInstance().ValidateUsername(login.AccountName))
                {
                    login.InvalidCredentials();
                }
                else
                {
                    // 2. Get user's salt
                    string salt = DataSourceSingleton.GetInstance().GetSalt(login.AccountName); // should not throw b/c account name validated
                    // 3. Hash password with salt
                    string passwordHash = new SHA256Hasher().GetHash(login.Password, salt);
                    // 4. Validate credentials (username and hashed password)
                    SelectedAccount = DataSourceSingleton.GetInstance().ValidateCredentials(login.AccountName, passwordHash);
                    if (login.DialogResult == DialogResult.OK && !IsValidAccount)
                    {
                        login.InvalidCredentials();
                    }
                    else if (login.DialogResult == DialogResult.OK && IsValidAccount)
                    {
                        DataSourceSingleton.GetInstance().Account = SelectedAccount;
                        login.Dispose();
                    }
                }
            } while (!IsValidAccount);
        }

        /// <summary>
        /// ICommand for Login
        /// </summary>
        public ICommand UpdateLogin
        {
            get { return new RelayCommand(LoginExecute, CanLoginExecute); }
        } 
        #endregion

        #region Logout
        public bool CanLogoutExecute()
        {
            return IsValidAccount;
        }

        private void LogoutExecute()
        {
            SelectedAccount = null;
        }

        public ICommand UpdateLogout
        {
            get { return new RelayCommand(LogoutExecute, CanLogoutExecute); }
        } 
        #endregion

        #region Change Data Source
        /// <summary>
        /// Determines when change data is enabled
        /// <returns>bool</returns>
        public bool CanChangeDataExecute()
        {
            return true;
        }

        /// <summary>
        /// Executes change data procedures
        /// </summary>
        private void ChangeDataExecute()
        {
            OpenFileDialog browseFile = new OpenFileDialog();
            browseFile.Filter = "XML Files (*.xml)|*.xml";
            browseFile.Title = "Browse XML file";
            if (browseFile.ShowDialog() == DialogResult.Cancel)
            {
                return;
            }
            try
            {
                bool successful = DataSourceSingleton.GetInstance().SetDataSource(browseFile.FileName);
                if (!successful)
                {
                    MessageBox.Show("File is not properly formatted", "File Error",
                        MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
                }
                else
                {
                    DataSourceSingleton.GetInstance().RefreshXmlData();
                    SelectedAccount = null;
                }
            }
            catch (Exception ex)
            {
                MessageBox.Show(ex.ToString(), "File Error",
                    MessageBoxButtons.OK, MessageBoxIcon.Exclamation);
            }
        }

        /// <summary>
        /// ICommand for Login
        /// </summary>
        public ICommand UpdateChangeData
        {
            get { return new RelayCommand(ChangeDataExecute, CanChangeDataExecute); }
        } 
        #endregion

        #region Open Account
        public bool CanOpenAccountExecute()
        {
            // TODO: Currently no permissions for being able to open accounts
            return true;
        }

        private void OpenAccountExecute()
        {
            FormOpenAccount formOpenAccount = new FormOpenAccount();
            formOpenAccount.ShowDialog();
            formOpenAccount.Dispose();
        }

        public ICommand UpdateOpenAccount
        {
            get { return new RelayCommand(OpenAccountExecute, CanOpenAccountExecute); }
        } 
        #endregion

        #region Modify Account
        public bool CanModifyAccountExecute()
        {
            if (IsValidAccount)
            {
                return true;
            }
            return false;
        }

        private void ModifyAccountExecute()
        {
            FormModifyAccount formModifyAccount = new FormModifyAccount();
            formModifyAccount.ShowDialog();
            formModifyAccount.Dispose();
        }

        public ICommand UpdateModifyAccount
        {
            get { return new RelayCommand(ModifyAccountExecute, CanModifyAccountExecute); }
        } 
        #endregion

        public bool CanAdminExecute()
        {
            if (IsValidAccount && SelectedAccount.IsAdmin)
            {
                return true;
            }
            return false;
        }

        private void AdminExecute()
        {
            FormAdmin formAdmin = new FormAdmin();
            formAdmin.ShowDialog();
            formAdmin.Dispose();
        }

        public ICommand UpdateAdmin
        {
            get { return new RelayCommand(AdminExecute, CanAdminExecute); }
        }

        #endregion RelayCommand Methods

        #region INotifyPropertyChanged Members

        public event PropertyChangedEventHandler PropertyChanged;

        public void OnPropertyChanged(string name)
        {
            PropertyChangedEventHandler handler = PropertyChanged;
            if (handler != null)
            {
                handler(this, new PropertyChangedEventArgs(name));
            }
        }
        #endregion
    }
}
